<?php

declare(strict_types=1);

namespace Haozing\FastCore\Command;

use Exception;
use Hyperf\Command\Annotation\Command;
use Hyperf\Database\Commands\Ast\ModelRewriteConnectionVisitor;
use Hyperf\Database\Commands\Ast\ModelUpdateVisitor;
use Hyperf\Database\Commands\ModelCommand as HyperfModelCommand;
use Hyperf\Database\Commands\ModelData;
use Hyperf\Database\Commands\ModelOption;
use Hyperf\Stringable\Str;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\CloningVisitor;
use Symfony\Component\Console\Input\InputOption;
use function Hyperf\Support\make;

#[Command(name: "hei:gen")]
class ServiceCommand  extends HyperfModelCommand
{
    protected string $pluginName;
    protected string $pluginPath;
    protected string $namespace;

    public function handle(): void
    {
        //询问用户是创建内部模块还是外部应用或者是插件？
        $module = $this->ask('请输入模块类型[1:插件应用,2:核心模块]', '1');

        if ($module != 1) {
            $module = $this->ask('请输入核心模块的名称', 'demo');
            $this->pluginName = $module;
            //将模块名首字母大写
            $this->pluginPath = BASE_PATH . DIRECTORY_SEPARATOR  . 'internal' . DIRECTORY_SEPARATOR . Str::studly($module);
            $this->namespace = 'Internal\\'. Str::studly($module);
        }else{
            // 读取composer.json。获取所有安装的插件
            $composerJson = json_decode(file_get_contents(BASE_PATH . '/composer.json'), true);
            $require = $composerJson['require'];
            $require = array_keys($require);
            $require = array_filter($require, function ($item) {
                return (strpos($item, 'plugins/') !== false) || (strpos($item, 'apps/') !== false);
            });
            $require = array_values($require);
            //将模型列表编号显示
            $this->output->writeLn('Please select the plugin you want to install');
            for ($i = 0; $i < count($require); $i++) {
                $this->output->writeLn($i . ':' . $require[$i]);
            }
            $select = $this->ask('Please enter the number of the plugin you want to generate', '0');
            if (is_numeric($select) && $select >= 0 && $select < count($require)) {
                //todo 获取需要拆卸的插件，获取插件的详细信息
                $selectModule = $require[$select];
                //从$selectModule中解析出appName、moduleName、
                $type = explode('/', $selectModule)[0];

                //判断type是apps还是plugins
                if ($type == 'apps'){
                    $app = explode('/', $selectModule)[1];
                    $apps = explode('.', $app);
                    $this->pluginName = $app;
                    $this->pluginPath = BASE_PATH . DIRECTORY_SEPARATOR  . 'apps' . DIRECTORY_SEPARATOR . Str::lower($apps[0]). DIRECTORY_SEPARATOR . Str::lower($apps[1]). DIRECTORY_SEPARATOR .'src';
                    $this->namespace = 'Apps\\'. Str::studly($apps[0]) . '\\'. Str::studly($apps[1]);
                }else{
                    $plugin = explode('/', $selectModule)[1];
                    $this->pluginName = $plugin;
                    $this->pluginPath = BASE_PATH . DIRECTORY_SEPARATOR  . 'plugins' . DIRECTORY_SEPARATOR . Str::lower($plugin). DIRECTORY_SEPARATOR .'src';
                }
            }
        }
        try {
            parent::handle();
        } catch (Exception $exception) {
            $this->output->writeln($exception->getMessage());
        }
    }
    protected function createModel(string $table, ModelOption $option): void
    {

        if (empty($table)){
            $table = 'test';
        }
        $columns = [];


        if ($this->input->getOption('gm')){
            //todo 生成错误码
            //设置父类
            $option->setUses("Haozing\FastCore\Model\Model");
            $option->setInheritance("Model");
            //设置casts
            $option->setForceCasts(true);
            $option->setRefreshFillable(true);
            //设置注释
            $option->setWithComments(true);

            $builder = $this->getSchemaBuilder($option->getPool());
            $table = Str::replaceFirst($option->getPrefix(), '', $table);
            $columns = $this->formatColumns($builder->getColumnTypeListing($table));

            $class = $option->getTableMapping()[$table] ?? Str::studly(Str::singular($table));
            $path = $this->pluginPath. DIRECTORY_SEPARATOR  . 'Service'. DIRECTORY_SEPARATOR . 'Model'. DIRECTORY_SEPARATOR . $class . '.php';

            $class = $this->namespace. "\\Service\\Model\\". $class;

            if (! file_exists($path)) {
                $this->mkdir($path);
                file_put_contents($path, $this->buildClass($table, $class, $option));
            }

            $columns = $this->getColumns($class, $columns, $option->isForceCasts());

            $traverser = new NodeTraverser();
            $traverser->addVisitor(make(ModelUpdateVisitor::class, [
                'class' => $class,
                'columns' => $columns,
                'option' => $option,
            ]));
            $traverser->addVisitor(make(ModelRewriteConnectionVisitor::class, [$class, $option->getPool()]));
            $data = make(ModelData::class, ['class' => $class, 'columns' => $columns]);
            foreach ($option->getVisitors() as $visitorClass) {
                $traverser->addVisitor(make($visitorClass, [$option, $data]));
            }

            $traverser->addVisitor(new CloningVisitor());

            $originStmts = $this->astParser->parse(file_get_contents($path));
            $originTokens = $this->lexer->getTokens();
            $newStmts = $traverser->traverse($originStmts);
            $code = $this->printer->printFormatPreserving($newStmts, $originStmts, $originTokens);

            file_put_contents($path, $code);
            $this->output->writeln(sprintf('<info>Model %s was created.</info>', $class));

            if ($option->isWithIde()) {
                $this->generateIDE($code, $option, $data);
            }

        }

        if ($this->input->getOption('gs')){
            //todo 生成错误码
            //生成服务
            $this->createService($table);

        }

        if ($this->input->getOption('gc')){
            //todo 生成错误码
            $this->createController($this->pluginName, $table,$columns);
        }

        if ($this->input->getOption('gl')){
            //todo 生成错误码
            $this->createLogic($table,$columns);
        }

    }

    protected function configure(): void
    {
        $this->addOption('gm','m', InputOption::VALUE_NONE, '是否生成model');
        $this->addOption('gs','s', InputOption::VALUE_NONE, '是否生成服务');
        $this->addOption('gc','c', InputOption::VALUE_NONE, '是否生成controller[包含验签]');
        $this->addOption('gl','l', InputOption::VALUE_NONE, '是否生成logic');
        parent::configure();
    }

    /**
     * Build the class with the given name.
     */
    protected function buildClass(string $table, string $name, ModelOption $option): string
    {
        $stub = file_get_contents(__DIR__ . '/stubs/Model.stub');

        return $this->replaceNamespace($stub, $name)
            ->replaceInheritance($stub, $option->getInheritance())
            ->replaceConnection($stub, $option->getPool())
            ->replaceUses($stub, $option->getUses())
            ->replaceClass($stub, $name)
            ->replaceTable($stub, $table);
    }
    protected function createService(string $table): void
    {
        $path = $this->pluginPath.  DIRECTORY_SEPARATOR. 'Service'. DIRECTORY_SEPARATOR . Str::studly(Str::singular($table)) . 'Service.php';

        $gm = $this->input->getOption('gm');
        if ($gm) {
            $stub = file_get_contents(__DIR__ . '/stubs/Service.stub');
        }else{
            $stub = file_get_contents(__DIR__ . '/stubs/Service_nogm.stub');
        }
        $fileContent = str_replace(
            ['%NAME%', '%MODEL%'],
            [Str::studly($this->namespace), Str::studly(Str::singular($table))],
            $stub
        );
        if (! file_exists($path)) {
            $this->mkdir($path);
            file_put_contents($path, $fileContent);
        }
    }
    //createApi
    protected function createController(string $pluginName, string $table,array $columns): void
    {
        $path = $this->pluginPath. DIRECTORY_SEPARATOR .'Controller'. DIRECTORY_SEPARATOR . Str::studly(Str::singular($table)) . 'Controller.php';

        $validate = '';
        $validateMsg = '';
        foreach ($columns as $column){
            $columnName = $column['column_name'];
            if ($column['column_key'] != 'PRI' && $column['column_name'] != 'updated_at' && $column['column_name'] != 'deleted_at'){

                $validate .= "                '{$columnName}' => 'required',\n";
                $validateMsg .= "                '{$columnName}.required' => '{$column['column_comment']}不能为空',\n";
            }
        }
        $gl = $this->input->getOption('gl');
        if ($gl) {
            $stub = file_get_contents(__DIR__ . '/stubs/Controller.stub');
        }else{
            $stub = file_get_contents(__DIR__ . '/stubs/Controller_nogl.stub');
        }
        $fileContent = str_replace(
            ['%NAME%', '%MODEL%', '%MODELX%','%VALIDATE%','%VALIDATEMSG%','%XNAME%'],
            [Str::studly($this->namespace), Str::studly(Str::singular($table)),Str::lcfirst(Str::studly(Str::singular($table))),$validate,$validateMsg,Str::lcfirst(Str::studly($pluginName))],
            $stub
        );
        if (! file_exists($path)) {
            $this->mkdir($path);
            file_put_contents($path, $fileContent);
        }
    }

    //createLogic
    protected function createLogic(string $table,array $columns): void
    {
        $path = $this->pluginPath. DIRECTORY_SEPARATOR.'Logic'. DIRECTORY_SEPARATOR. Str::studly(Str::singular($table)). 'Logic.php';

        $validate = '';
        $validateMsg = '';
        $addValidate = '';
        $updateValidate = '';
        $listValidate = '';
        foreach ($columns as $column){
            $columnName = $column['column_name'];
            if ($column['column_key'] != 'PRI' && $column['column_name'] != 'updated_at' && $column['column_name'] != 'deleted_at'){

                $validate .= "                '{$columnName}' => 'required',\n";
                $validateMsg .= "                '{$columnName}.required' => '{$column['column_comment']}不能为空',\n";
                //暂时只判断int、字符串、时间
                //判断$column['column_type']字符串是否包含int，如果包含，则添加int验证，判断是否包含time，如果包含，则添加时间验证，否则添加字符串验证
                if (Str::contains($column['column_type'],'int')){
                    $addValidate.= '            \''.$column['column_name'].'\' => $params[\''.$columnName.'\'] ?? 0,'."\n";
                    $updateValidate.= '        if (isset($params[\''.$columnName.'\'])) {'."\n".'            $data[\''.$column['column_name'].'\'] = $params[\''.$columnName.'\'];'."\n".'        }'."\n";
                    $listValidate.= '        if (isset($params[\''.$columnName.'\']) && $params[\''.$columnName.'\']) {'."\n".'            $where[] = [\''.$column['column_name'].'\',$params[\''.$columnName.'\']];'."\n".'        }'."\n";

                    continue;
                }
                if ($column['column_name'] == 'created_at'){
                    $addValidate.= '            \''.$column['column_name'].'\' => date(\'Y-m-d H:i:s\'),'."\n";
                    $updateValidate.= '        if (isset($params[\''.$columnName.'\'])) {'."\n".'            $data[\''.$column['column_name'].'\'] = $params[\''.$columnName.'\'];'."\n".'        }'."\n";
                    $listValidate.= '        if (isset($params[\''.$columnName.'\']) && $params[\''.$columnName.'\']) {'."\n".'            $where[] = [\''.$column['column_name'].'\',$params[\''.$columnName.'\']];'."\n".'        }'."\n";
                    continue;
                }
                $addValidate.= '            \''.$column['column_name'].'\' => $params[\''.$columnName.'\'] ?? \'\','."\n";
                $updateValidate.= '        if (isset($params[\''.$columnName.'\'])) {'."\n".'            $data[\''.$column['column_name'].'\'] = $params[\''.$columnName.'\'];'."\n".'        }'."\n";
                $listValidate.= '        if (isset($params[\''.$columnName.'\']) && $params[\''.$columnName.'\']) {'."\n".'            $where[] = [\''.$column['column_name'].'\',$params[\''.$columnName.'\']];'."\n".'        }'."\n";

            }
        }
        $gs = $this->input->getOption('gs');
        if ($gs) {
            $stub = file_get_contents(__DIR__ . '/stubs/Logic.stub');
        }else{
            $stub = file_get_contents(__DIR__ . '/stubs/Logic_nogs.stub');
        }
        $stub = str_replace(
            ['%NAME%', '%MODEL%', '%MODELX%','%ADDVALIDATE%','%UPDATEVALIDATE%','%LISTVALIDATE%'],
            [Str::studly($this->namespace), Str::studly(Str::singular($table)),Str::lcfirst(Str::studly(Str::singular($table))),$addValidate,$updateValidate,$listValidate],
            $stub
        );
        if (! file_exists($path)) {
            $this->mkdir($path);
            file_put_contents($path, $stub);
        }
    }
}
